package com.atguigu.tingshu.order.adapter.impl;

import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.message.LocalMsg;
import com.atguigu.tingshu.order.adapter.PayWay;
import com.atguigu.tingshu.order.mapper.LocalMsgMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * Description:
 *
 * @author Yanxinhao on 2025/2/11
 */
@Component
@Slf4j
public class RechargePayWayImpl implements PayWay {

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private LocalMsgMapper localMsgMapper;


    /**
     * 定时任务
     * 定时任务，每一小时扫描一次本地消息表，如果消息状态是0，那么就进行消息的解锁操作
     */
    @Scheduled(fixedDelay = 1000 * 60 * 60)
    public void scanLocalMsg(){
        //查找出所有状态为0的本地消息
        LambdaQueryWrapper<LocalMsg> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LocalMsg::getStatus,0);
        List<LocalMsg> localMsgList = localMsgMapper.selectList(wrapper);

        for (LocalMsg localMsg : localMsgList) {
            //进行消息重发
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_UNLOCK, localMsg.getMsgContent());
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT, MqConst.ROUTING_ACCOUNT_MINUS, localMsg.getMsgContent());
        }


    }


    /**
     * 判断当前的支付方式是否是余额支付
     * @param payWay 支付方式
     * @return 是否是余额支付
     */
    @Override
    public Boolean isThisWay(String payWay) {
        return "1103".equals(payWay);
    }


    /**
     * 余额结算业务
     * @param orderInfoVo 订单信息
     * @param orderNo 订单号
     * @param userId 用户id
     */
    @Override
    public void payWay(OrderInfoVo orderInfoVo, String orderNo, Long userId) {
        //1.判断当前用户的余额是否够用
        //1.1 组装锁定订单信息对象
        AccountLockVo accountLockVo = prepareAccountLockVo(orderInfoVo,orderNo,userId);
        //1.2 根据锁定订单vo去远程调用account微服务，去判断是否余额足够支持本次金额锁定，即允许购买
        Result<AccountLockResultVo> lockAccountResult = userAccountFeignClient.checkAndLockAccountMoney(accountLockVo);
        Integer lockCode = lockAccountResult.getCode();
        if (!(lockCode == 200)){
            throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
        }
        try {
            //2.保存订单信息
            orderInfoService.saveOrderInfo(orderInfoVo,orderNo,userId);

            //2.1 初始化本地消息表
            orderInfoService.saveLocalMsg(orderNo);

            //2.2 发生异步消息，通知下游消费者去真正的扣减用户的余额
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT,MqConst.ROUTING_ACCOUNT_MINUS,orderNo);

            //2.3 支付成功，现在我们需要去修改数据库的内容
            // 将订单状态改为支付成功
            // 修改专辑以及声音的购买量 - 0402
            // 记录订单支付流水
            // 修改Elastic数据库中的专辑的购买量
            orderInfoService.paySuccess(orderNo);


        } catch (GuiguException e) {
            //3.如果在执行上述代码遇到了错误，那么就会导致分布式事务问题
            // 在远程调用user微服务的checkAndLockAccountMoney方法的时候，我们已经做了锁定余额的操作，即对数据表做了操作
            // 此时在保存订单消息以及下面的操作如果执行失败了，就会导致数据库数据不一致
            // 这里我们选择最简单清晰思路的解决方案，捕捉到异常，如果执行失败我们就执行回退操作
            // 这里我们执行的回退操作就是，将我们锁定余额的操作，该回去，我们选择使用异步消息队列
            log.error("在执行保存订单信息及扣减用户账户余额的业务中出现了异常，回退锁定余额操作");
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT,MqConst.ROUTING_ACCOUNT_UNLOCK,orderNo);
        } catch (Exception e){
            rabbitService.sendMessage(MqConst.EXCHANGE_ACCOUNT,MqConst.ROUTING_ACCOUNT_MINUS,orderNo);
        }
    }

    /**
     * 准备组装订单锁定金额对象
     * @param orderInfoVo 订单信息
     * @param orderNo 订单号
     * @param userId 用户id
     * @return
     */
    private AccountLockVo prepareAccountLockVo(OrderInfoVo orderInfoVo, String orderNo, Long userId) {
        AccountLockVo accountLockVo = new AccountLockVo();
        accountLockVo.setOrderNo(orderNo);
        accountLockVo.setUserId(userId);
        accountLockVo.setAmount(orderInfoVo.getOrderAmount());
        accountLockVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());

        return accountLockVo;
    }
}